
#include "Scope.h"
#include "SymbolTable.h"

namespace mjc {

	Scope::ScopeData::ScopeData(const Scope::ScopeData& sd)
		:outer(NULL),
		locals(sd.locals)
	{
		if(sd.outer)
			outer = new ScopeData(*sd.outer);//recursive call
	}

	Scope::ScopeData& Scope::ScopeData::operator = (const Scope::ScopeData& sd)
	{
		locals = sd.locals;
		
		if(sd.outer)
		{
			if(!outer)
				outer = new ScopeData(*sd.outer);
			else
				*outer = *sd.outer;
		}
		else if(outer)
			delete outer;

		return *this;
	}

	Scope::ScopeData::~ScopeData()
	{
		if(outer)
			delete outer;//recursive call
	}

	Scope::Scope()
		:scopeData(new ScopeData),
		level(-1)
	{}

	Scope::Scope(const mjc::Scope &other)
		:scopeData(NULL),
		level(other.level)
	{
		scopeData = new ScopeData(*other.scopeData);
	}

	Scope& Scope::operator = (const Scope& other)
	{
		*scopeData = *other.scopeData;
		level = other.level;
		return *this;
	}

	Scope::~Scope()
	{
		delete scopeData;
	}

	void Scope::OpenScope()
	{
		++level;
		ScopeData* ns = new ScopeData;
		ns->outer = scopeData;
		scopeData = ns;
	}

	void Scope::CloseScope()
	{
		--level;
		ScopeData* cs = scopeData;
		scopeData = scopeData->outer;
		cs->outer = NULL;//to prevent the recursive destructor from deleting everything else after
		delete cs;
	}

	int Scope::AddObject(Object& object)
	{
		std::vector<Object>& locals = scopeData->locals;

		if(object.GetKind() == Object::K_VARIABLE)
		{
			object.SetVariableData(Object::VariableData(locals.size(), level));
		}
		
		for(std::vector<Object>::iterator it = locals.begin(); it != locals.end(); ++it)
		{
			if(it->GetName() == object.GetName())
				return 0;
		}
		
		locals.push_back(object);
		return 1;
	}

	Object* Scope::Find(const std::wstring& name)
	{
		ScopeData* sd = scopeData;

		while(sd)
		{
			std::vector<Object>& locals = sd->locals;
			for(unsigned int i=0; i<locals.size(); ++i)
				if(locals[i].GetName() == name)
					return &locals[i];
			sd = sd->outer;
		}

		return NULL;
	}
	
	std::wostream& operator << (std::wostream& os, const Scope& scope)
	{
		int level = scope.level;

		for(Scope::ScopeData* sd = scope.scopeData; sd != NULL; sd = sd->outer)
		{
			os << "Scope level: " << level << std::endl;
			--level;

			std::vector<Object>& locals = sd->locals;
			for(unsigned int i=0; i<locals.size(); ++i)
				os << L"[" << locals[i] << L"]" << std::endl;
		}

		return os;
	}
}